define([
    'js/resolve',
    'showdown',
    'highlight',
    'ng-showdown',
    'js/components/dialog',
    'js/components/aceeditor'
], function(resolve, showdown) {
    angular.module('app.markdown', ['ng-showdown', 'app.dialog', 'app.aceeditor'])
        .config(markdownConfig)
        .directive('markdown', markdownDirective)
        .run(function($showdown, _dialog, $templateCache) {
            // load markdown template
            $templateCache.put('templates/extendmenu/markdown.html',
                '<li class="dropdown-submenu hover">' +
                '  <button class="btn btn-default"><i class="fa fa-code pull-left"></i> 格式化</button>' +
                '  <ul class="dropdown-menu" style="min-width:180px">' +
                '    <li><button class="btn btn-default" ng-click="extendmenu.format(\'bold\')">' +
                '        <i class="fa fa-bold pull-left"></i> 粗体</button></li>' +
                '    <li><button class="btn btn-default" ng-click="extendmenu.format(\'italic\')">' +
                '        <i class="fa fa-italic pull-left"></i> 斜体</button></li>' +
                '    <li><button class="btn btn-default" ng-click="extendmenu.format(\'underline\')">' +
                '        <i class="fa fa-underline pull-left"></i> 下划线</button></li>' +
                '    <li><button class="btn btn-default" ng-click="extendmenu.format(\'strikethrough\')">' +
                '        <i class="fa fa-strikethrough pull-left"></i> 删除线</button></li>' +
                '  </ul>' +
                '</li>' +
                '<li><button class="btn btn-default" ng-click="extendmenu.preview()">' +
                '    <i class="fa fa-eye pull-left"></i> 预览</button></li>');
            $templateCache.put('templates/extendbar/markdown.html',
                '<div class="btn-group">' +
                '  <headerlist on-selected="extendbar.header.click"></headerlist>' +
                '  <hyperlink on-selected="extendbar.link.click"></hyperlink>' +
                '  <imageupload on-selected="extendbar.image.click"></imageupload>' +
                '  <tablepicker position="\'right\'" on-selected="extendbar.table.click"></tablepicker>' +
                '  <sourcecode position="\'right\'" on-selected="extendbar.source.click"></sourcecode>' +
                '</div>');

            // show image modal
            if (!window['__showImage']) {
                $(document.head).append(
                        '<style>' +
                        '  .image-modal-open { overflow: hidden; }' +
                        '  .image-modal.modal { overflow: auto; }' +
                        '  .image-modal .modal-dialog { position: absolute; left: -10000px; margin: 0; width: auto; }' +
                        '</style>');
                var jqWindow = $(window),
                    showAndResizeModal = function() {
                        var jqModal = $('.image-modal .modal-dialog');
                        var scrollY = parseInt($('body').css('padding-right'));
                        if (jqModal.width() < jqWindow.width() - scrollY) {
                            jqModal.css('left', (jqWindow.width() - scrollY - jqModal.width()) / 2 + 'px');
                            $('.image-modal.modal').scrollLeft(0);
                        } else {
                            jqModal.css('left', '0');
                            $('.image-modal.modal').scrollLeft((jqModal.width() - (jqWindow.width() - scrollY)) / 2);
                        }
                        if (jqModal.height() < jqWindow.height()) {
                            var top = (jqWindow.height() - jqModal.height()) / 2;
                            if (jqWindow.width() > 768 && top > 100) {
                                top = 100;
                            }
                            jqModal.css('top', top + 'px');
                        } else {
                            jqModal.css('top', '0');
                        }
                    };
                window['__showImage'] = function(url) {
                    _dialog.modal({
                        backdrop: 'static',
                        openedClass: 'image-modal-open',
                        windowClass: 'image-modal',
                        size: 'md',
                        template: 
                            '<div ng-init="init()" class="modal-body">' +
                            '  <img style="cursor:zoom-out" ng-click="close()">' +
                            '</div>',
                        controller: ['$scope', '$uibModalInstance', function ($scope, $uibModalInstance) {
                            $scope.init = function() {
                                jqWindow.bind('resize', showAndResizeModal);
                                $('.image-modal .modal-dialog img').on('load', showAndResizeModal).attr('src', url);
                            };
                            $scope.close = function() {
                                $uibModalInstance.close('close');
                                jqWindow.unbind('resize', showAndResizeModal);
                            };
                        }]
                    });
                };
            }
            // markdown options
            angular.extend($showdown.getOptions(), {
                ghCompatibleHeaderId: true,
                prefixHeaderId: true,
                rawHeaderId: true,
                rawPrefixHeaderId: true,
                parseImgDimensions: true,
                simplifiedAutoLink: true,
                strikethrough: true,
                tables: true,
                tablesHeaderId: true,
                ghCodeBlocks: true,
                tasklists: true,
                smoothLivePreview: true,
                smartIndentationFix: true,
                openLinksInNewWindow: true,
                backslashEscapesHTMLTags: true,
                emoji: true,
                underline: true,
                completeHTMLDocument: false,
                metadata: true,
                splitAdjacentBlockquotes: true
            });
        });

    function markdownConfig($showdownProvider) {
        // highlight.js
        showdown.extension('highlight', function() {
            const classAttr = 'class="';
            const decodeHtml = function(text) {
                return text
                    .replace(/&amp;/g, '&')
                    .replace(/&lt;/g, '<')
                    .replace(/&gt;/g, '>')
                    .replace(/&nbsp;/g ,' ')
                    .replace(/&#39;/g, '\'')
                    .replace(/&quot;/g, '"');
            }
            //
            return [{
                type: 'output',
                filter: function(text, converter, options) {
                    var left  = '<pre><code\\b[^>]*>',
                        right = '</code></pre>',
                        flags = 'g',
                        replacement = function(wholeMatch, match, left, right) {
                            match = decodeHtml(match);
                            var lang = (left.match(/class=\"([^ \"]+)/) || [])[1];

                            if (left.includes(classAttr)) {
                                var attrIndex = left.indexOf(classAttr) + classAttr.length;
                                left = left.slice(0, attrIndex) + 'hljs ' + left.slice(attrIndex);
                            } else {
                                left = left.slice(0, -1) + ' class="hljs">';
                            }

                            if (lang && hljs.getLanguage(lang)) {
                                return left + hljs.highlight(lang, match).value + right;
                            } else {
                                return left + hljs.highlightAuto(match).value + right;
                            }
                        };

                    return showdown.helper.replaceRecursiveRegExp(text, replacement, left, right, flags);
                }
            }]
        });

        // bootstrap
        showdown.extension('bootstrap', function() {
            return [{
                type: 'output',
                filter: function(text, converter, options) {
                    return text.replace(/<table>/g, '<table class="table table-bordered">')
                        .replace(/<img ([^>]+)>/g, function(whole, match) {
                            var url = /src="([^"]+)"/g.exec(match)[1];
                            return '<img ' + match + ' onclick="__showImage(\'' + url + '\')" style="cursor:zoom-in">';
                        });
                }
            }]
        });

        // load extensions
        $showdownProvider.loadExtension('highlight');
        $showdownProvider.loadExtension('bootstrap');
    }

    function markdownDirective($rootScope, $compile, $templateCache, $timeout, _dialog) {
        return resolve({
            restrict: 'E',
            replace: true,
            templateUrl: 'js/templates/markdown.html',
            scope: {
                file: '=',
                context: '='
            },
            compile: function() {
                var toolscope = $rootScope.$new(true);
                toolscope.extendbar = {
                    'header': { click: null },
                    'unorder': { click: null },
                    'order': { click: null },
                    'link': { click: null },
                    'image': { click: null },
                    'table': { click: null },
                    'source': { click: null }
                };
                var template = $templateCache.get('templates/extendbar/markdown.html');
                var extendbar = $($compile(template)(toolscope));
                extendbar.css('display', 'none');
                extendbar.appendTo($('.extendbar'));

                function resize(element, position) {
                    var percent = 0;
                    var zoneWidth = element.find('.move-zone').width();
                    if (position.left <= 0) {
                        percent = 20;
                        element.find('.separator').css('left', '0');
                    } else if (position.left >= zoneWidth) {
                        percent = 80;
                        element.find('.separator').css('left', zoneWidth + 'px');
                    } else {
                        percent = (position.left / zoneWidth) * 60 + 20;
                        element.find('.separator').css('left', (position.left / zoneWidth * 100) + '%');
                    }
                    element.find('>div:eq(0)').css('width', percent + '%');
                    element.find('>div:eq(2)').css('width', (100 - percent) + '%');
                }

                function handleResize(element) {
                    if (element.is(':visible')) {
                        $timeout(function() {
                            var position = element.find('.separator').position();
                            resize(element, { left: position.left });
                        });
                    }
                }

                return {
                    pre: function(scope, element) {
                        scope.extend = {
                            //template: '',
                            templateUrl: 'templates/extendmenu/markdown.html',
                            renderer: function(editor) {
                                this.extendmenu= {
                                    'format': function(type) {
                                        var cursor = editor.selection.getCursor();
                                        var text = editor.getSession().getTextRange(editor.getSelectionRange());
                                        //
                                        var selection = {
                                            text: '',
                                            offset: 0
                                        };
                                        switch(type) {
                                        case 'bold':
                                            selection.text = '**' + text + '**';
                                            selection.offset = 2;
                                            break;
                                        case 'italic':
                                            selection.text = '*' + text + '*';
                                            selection.offset = 1;
                                            break;
                                        case 'underline':
                                            selection.text = '__' + text + '__';
                                            selection.offset = 2;
                                            break;
                                        case 'strikethrough':
                                            selection.text = '~~' + text + '~~';
                                            selection.offset = 2;
                                            break;
                                        }
                                        //
                                        editor.focus();
                                        editor.insert(selection.text);
                                        editor.moveCursorTo(cursor.row, cursor.column + selection.offset);
                                    },
                                    'preview': function() {
                                        _dialog.modal({
                                            size: 'lg',
                                            windowClass: 'markdown-preview',
                                            template: '<div class="markdown preview">' +
                                                      '  <button type="button" class="close" ng-click="close()">&times;</button>' +
                                                      '  <p markdown-to-html="code"></p>' +
                                                      '</div>',
                                            controller: ['$scope', '$uibModalInstance', function ($scope, $uibModalInstance) {
                                                $scope.code = scope.code;
                                                $scope.close = function() {
                                                    $uibModalInstance.close();
                                                }
                                            }]
                                        });
                                    }
                                };
                            }
                        };
                    },
                    post: function(scope, element) {
                        scope.code = '';
                        scope.change = function(code) {
                            scope.code = code;
                        }
                        //
                        element.find('.separator').draggable({
                            axis: 'x',
                            delay: 200,
                            containment: 'parent',
                            drag: function(event, ui) {
                                resize(element, { left: ui.position.left });
                                $timeout(function() {
                                    scope.$broadcast('@resize.editor');
                                });
                            }
                        });

                        scope.renderer = function(helper, editor) {
                            // extendbar
                            extendbar.css('display', '');
                            toolscope.extendbar['header'].click = function(level) {
                                var code = '';
                                for (var i = 1; i <= level; i++) {
                                    code += '#';
                                }
                                editor.focus();
                                editor.insert(code + ' ');
                            };
                            toolscope.extendbar['link'].click = function(link) {
                                editor.focus();
                                editor.insert('[' + link.text + '](' + link.url + ' "")');
                            };
                            toolscope.extendbar['image'].click = function(image) {
                                editor.focus();
                                editor.insert('![图片](' + image.url + ' "' + image.name + '")');
                            };
                            toolscope.extendbar['table'].click = function(row, column) {
                                var codes = [];
                                for (var i = 1; i <= row; i++) {
                                    var code = '|';
                                    for (var j = 1; j <= column; j++) {
                                        code += '   |';
                                    }
                                    codes.push(code)
                                    if (i == 1) {
                                        code = '|';
                                        for (var j = 1; j <= column; j++) {
                                            code += '---|';
                                        }
                                        codes.push(code)
                                    }
                                }
                                editor.focus();
                                editor.insert(codes.join('\n'));
                            };
                            toolscope.extendbar['source'].click = function(action, code, lang) {
                                var cursor = editor.selection.getCursor();
                                var selection = {
                                    text: code,
                                    offset: 0
                                };
                                if (action == 'hr') {
                                    selection.text = '----------\n';
                                } else if (action == 'blockquote') {
                                    selection.text = '> ';
                                } else if (action == 'code') {
                                    selection.text = '``';
                                    selection.offset = 1;
                                } else if (action == 'blockcode') {
                                    selection.text = '```' + (lang ? lang : '') + '\n' + code + '\n```';
                                }
                                //
                                editor.focus();
                                editor.insert(selection.text);
                                editor.moveCursorTo(cursor.row, cursor.column + selection.offset);
                            };
                        }

                        // resize
                        scope.$watch(function() {
                            return [element.width(), element.height()];
                        }, function() {
                            handleResize(element);
                        }, true);
                        scope.$on('@resize.all', function(event) {
                            handleResize(element);
                        });
                    }
                };
            }
        });
    }
});
